#include "t_box_audio.h"

namespace BoxPlayer //[class T_Box_AudioDevice]
{
    T_Box_AudioDevice::T_Box_AudioDevice(T_Box_Media *a_media)
        : m_media(a_media)
        , m_audioOutput(0)
    {
        if(m_media)
        {
            QAudioDeviceInfo v_device = QAudioDeviceInfo::defaultOutputDevice();
            m_format.setFrequency(a_media->codecContex()->sample_rate);
            m_format.setChannels(a_media->codecContex()->request_channels);
            m_format.setSampleSize(16);
            m_format.setCodec("audio/pcm");
            m_format.setByteOrder(QAudioFormat::LittleEndian);
            m_format.setSampleType(QAudioFormat::SignedInt);
            Q_ASSERT(v_device.isFormatSupported(m_format));
            m_audioOutput = new QAudioOutput(v_device, m_format);
            m_audioOutput->setParent(this);
        }
    }
    T_Box_AudioDevice::~T_Box_AudioDevice()
    {
        this->stop();
        //delete m_audioOutput; //[m_audioOutput->setParent(this);]
    }
    void T_Box_AudioDevice::start(QIODevice *a_generator)
    {
        if(!m_audioOutput) return;
        if(m_audioOutput->state()==QAudio::ActiveState) return;
        m_audioOutput->stop();
        m_audioOutput->start(a_generator);
    }
    void T_Box_AudioDevice::stop()
    {
        if(!m_audioOutput) return;
        m_audioOutput->stop();
    }
    QAudioOutput *T_Box_AudioDevice::getQAudioOutput()
    {
        return m_audioOutput;
    }
};

namespace BoxPlayer //[class T_Box_AudioOutput]
{
    T_Box_AudioOutput::T_Box_AudioOutput(T_Box_Context *a_stream, QObject *parent)
        : QIODevice(parent)
        , m_stream(a_stream)
        , m_audioDevice(a_stream->audio())
        , m_out_read_size(0)
        , m_max_seek_size(0)
        , m_base_bytes(0)
        , m_base_msecs(-1)
    {
        //[open generator]
        bool v_ok = QIODevice::open(QIODevice::ReadOnly);
        qDebug() << "[T_Box_AudioOutput::Generator()]" << v_ok;
        m_stream->core()->seekManager()->addSeekable(this);
    }
    T_Box_AudioOutput::~T_Box_AudioOutput()
    {
        this->stop();
    }
    qint64 T_Box_AudioOutput::seekMe(T_Box_Seek_Manager *a_seek_mgr, qint64 a_msec) //[T_Box_Seekable]
    {
        Q_UNUSED(a_seek_mgr);Q_UNUSED(a_msec);
        m_base_msecs = -1;
        m_base_bytes = 0;
        return -1;
    }
    qint64 T_Box_AudioOutput::elapsed()
    {
        if(m_base_msecs < 0)
        {
            return -1;
        }
        QAudioOutput *v_QAudioOutput = m_audioDevice.getQAudioOutput();
        QAudioFormat v_format = v_QAudioOutput->format();
        qint64 v_bytesWaiting = (v_QAudioOutput->bufferSize() - v_QAudioOutput->bytesFree());
#if 0x0
        if(v_bytesWaiting == 0)
        {
            return -1;
        }
#endif
        qint64 v_bytesPlayed = m_out_read_size - v_bytesWaiting;
        qint64 v_msecsDelta =
                ((qint64)1000)
                * (v_bytesPlayed - m_base_bytes)
                / ( v_format.channels() * v_format.sampleSize() / 8 )
                / v_format.frequency();
        qint64 v_msecs = m_base_msecs + v_msecsDelta;
        return v_msecs;
    }
    void T_Box_AudioOutput::start()
    {
        m_audioDevice.start(this);
    }
    void T_Box_AudioOutput::stop()
    {
        m_audioDevice.stop();
    }
    // In qaudiooutput_win32_p.cpp
    // QAudioOutputPrivate::deviceReady() seek() backwards
    // (when part of the final read() data could not be written to the sound device)
    // T_Box_AudioOutput::m_audio_last_read (=QByteArray) holds final read() data to handle
    // this case.
    bool T_Box_AudioOutput::seek(qint64 pos)
    {
        qint64 v_curr_pos = this->pos();
        qint64 v_seek_len = v_curr_pos - pos;
        //qDebug() << "[T_Box_AudioOutput::seek()]" << v_seek_len << m_audio_last_read.size();
        if(v_seek_len <= 0)
        {
            qDebug() << "=====>seek() error (1)";
            return false;
        }
        m_max_seek_size = qMax(m_max_seek_size, v_seek_len);
        if(v_seek_len > m_audio_last_read.size())
        {
            qDebug() << "=====>seek() error (2)";
            m_out_read_size -= m_audio_last_read.size();
            m_audio_data_left.prepend(m_audio_last_read);
            m_audio_data_left.clear();
            return false;
        }
        m_out_read_size -= v_seek_len;
        m_audio_data_left.prepend(p_pop_back(m_audio_last_read, v_seek_len));
        //qDebug() << "=====>seek() ok";
        return true;
    }
    qint64 T_Box_AudioOutput::readData(char *data, qint64 len)
    {
        //qDebug() << "[T_Box_AudioOutput::readData()]" << "[this->pos()]" << this->pos() << "[len]" << len;
        Q_ASSERT(len%2==0);
        //[m_max_seek_size]
        m_max_seek_size = qMax(m_max_seek_size, len);
        //[left_len, read_len]
        qint64 prec_len = 0;
        qint64 need_len = len;
        //[pop from m_audio_data_left]
        QByteArray v_result = p_pop_front(m_audio_data_left, need_len);
        need_len -= v_result.size();
        prec_len += v_result.size();
        //[decode packet if needed]
        if(need_len>0)
        {
            QByteArray v_packet_bytes = p_readPacket(prec_len, need_len);
            need_len -= v_packet_bytes.size();
            prec_len += v_packet_bytes.size();
            v_result.append(v_packet_bytes);
        }
        //qDebug() << "[T_Box_AudioOutput::readData()]" << read_len << v_result.size();
        Q_ASSERT(prec_len==v_result.size());
        //[save bytes beyond capacity => m_audio_data_left]
        if(v_result.size() > len)
        {
            m_audio_data_left.append(p_pop_back(v_result, v_result.size()-len));
        }
        //[append the result bytes => m_audio_last_read (prepare for possible seek()-ing backwards)]
        m_audio_last_read.append(v_result);
        //[reduce m_audio_last_read's size => m_max_seek_size]
        if(m_audio_last_read.size() > m_max_seek_size)
        {
            m_audio_last_read = m_audio_last_read.right(m_max_seek_size);
        }
        //Q_ASSERT(v_result.size()<=len);
        m_out_read_size += v_result.size(); //add to m_out_read_size before padding zero.
#if 0x0
        if(v_result.size()<len)
        {
            //need padding zerro
            this->breakTimer();
            QByteArray v_pad(len-v_result.size(), (char)0);
            v_result.append(v_pad);
        }
#endif
        //qDebug() << "[T_Box_AudioOutput::readData()]" << len << v_result.size();
        memcpy(data, v_result.constData(), v_result.size());
        return v_result.size();
    }
    qint64 T_Box_AudioOutput::writeData(const char *data, qint64 len)
    {
        Q_UNUSED(data);Q_UNUSED(len);
        return 0;
    }
    qint64 T_Box_AudioOutput::bytesAvailable() const
    {
        return 0;
    }
    QByteArray T_Box_AudioOutput::p_readPacket(qint64 prec_len, qint64 need_len)
    {
        //[seek lock]
        T_RecursiveLocked *v_seek_mutex = m_stream->core()->seekManager();
        if(!v_seek_mutex->tryLockForRead())
        {
            return QByteArray();
        }
        T_READ_LOCKER(v_seek_locker, v_seek_mutex)
                v_seek_mutex->unlock();
        //
        if(!m_stream->core()->timeline()->isActive())
        {
            return QByteArray();
        }
        //
        if(m_stream->audio()->packetQueue()->size()>0 && m_stream->audio()->packetQueue()->head()->isEnd())
        {
            return QByteArray();
        }
        //[left_len, read_len]
        qint64 read_len = 0;
        QByteArray v_result;
        if(need_len>0 && m_stream->audio()->packetQueue()->size()>0)
        {
            QSharedPointer<T_AV_Packet> v_head_packet = m_stream->audio()->packetQueue()->head();
            m_base_bytes = m_out_read_size + prec_len;
            m_base_msecs = v_head_packet->timing();
        }
        while(need_len>0 && m_stream->audio()->packetQueue()->size()>0)
        {
            QSharedPointer<T_AV_Packet> v_head_packet = m_stream->audio()->packetQueue()->head();
            QByteArray v_audio_byes = v_head_packet->decodeAudio(&m_decode_buff);
            need_len -= v_audio_byes.size();
            read_len += v_audio_byes.size();
            v_result.append(v_audio_byes);
            m_stream->audio()->packetQueue()->dequeue();
        }
        //qDebug() << "[T_Box_AudioOutput::p_readPacket()]" << "read_len" << v_result.size();
        Q_ASSERT(read_len==v_result.size());
        // v_result.size() might exceed wanted_len.
        return v_result;
    }
    QByteArray T_Box_AudioOutput::p_pop_front(QByteArray &a_bytes, int a_maxlen)
    {
        int v_len = qMin(a_maxlen, a_bytes.size());
        QByteArray v_pop = a_bytes.left(v_len);
        a_bytes = a_bytes.right(a_bytes.size()-v_len);
        return v_pop;
    }
    QByteArray T_Box_AudioOutput::p_pop_back(QByteArray &a_bytes, int a_maxlen)
    {
        int v_len = qMin(a_maxlen, a_bytes.size());
        QByteArray v_pop = a_bytes.right(v_len);
        a_bytes = a_bytes.left(a_bytes.size()-v_len);
        return v_pop;
    }
};
